/**
 * ElementHandler.java
 * 
 * Peter Steiner and Fredrik Wallenius
 * DP08 - KTH - 2009
 */

package gameLogic;

import gameElements.GBall;
import gameElements.GBrick;
import gameElements.GSupriseBrick;
import gameElements.GfxElement;
import gameElements.GfxExplossion;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.Timer;

import adapterLayer.Adapter;

/**
 * The ElementHandler class is the keeper of the objects currently in play.
 * It is also responsible of the periodic update in the game loop.
 *
 */
public class ElementHandler {

	/**
	 * Reference to the current adapter object.
	 */
	protected Adapter adapter;
	private ArrayList<GfxElement> gfxElements; 
	private SurpriseGenerator gSP;		//suprice
	
	private int exitReason = -1, timeElapsed = 0, bricksLeft = 0, ballsInAir = 0;
	public static final int OUTOFBALLS = 1, BRICKSCLEARED = 2;
	
	private Timer gameTimer;
	private Boolean gameIsRunning;
	private Boolean gameIsOn;
		
	/**
	 * 
	 * @param adapter_ The adapter object to be used in this game.
	 */
	public ElementHandler(Adapter adapter_){
		//Contact with the graphical side of the world
		adapter = adapter_;
		adapter.setElementHandler(this);
		
		gfxElements = new ArrayList<GfxElement>();
		adapter.getGameRenderer().setElementList(gfxElements);
		gameTimer = new Timer(20, new gameLoop());				//50 times / s
		
		gameIsOn = true;
		gameIsRunning = true;
	}
	
	/**
	 * Sets the time elapsed for the game in play
	 * @param t Time in seconds.
	 */
	public void setTimeElapsed(int t){ timeElapsed = t; }
	public int getTimeElapsed(){ return timeElapsed; }
	public int getBricksLeft(){ return bricksLeft; }
	public int getBallsInAir(){ return ballsInAir; }
	
	/**
	 * Starts the game by starting the thread which fires
	 * the game loop a certain amount of times per second.
	 */
	public void Start(){
		gameIsOn = true;
		gameIsRunning = true;
		gameTimer.start();

		// start timer here
	}
	
	
	/**
	 * Set the surprise generator to be used in this game.
	 * 
	 * @param gSP_ A reference to the SurpriseGenerator object.
	 */
	public void setSurpriceGenerator(SurpriseGenerator gSP_){
		this.gSP = gSP_;
	}
	
	/**
	 * Adds a new GfxElement to the collection used in the current game. Needs to be
	 * done in a thread safe way (preferrably using javax.swing.Timer)
	 * 
	 * @param newElement A reference to the newly created GfxElement.
	 */
	public void addGfxElement(GfxElement newElement){
		gfxElements.add(newElement);
	}
	
	/**
	 * Return a reference to the current collection of GfxElements.
	 * 
	 * @return A reference to the current collection of GfxElements.
	 */
	public ArrayList<GfxElement> getElements(){
		return gfxElements;
	}
	
	/**
	 * Method for pausing the or resuming the game.
	 * 
	 */
	public void toggleOnOff(){
		if(this.gameTimer.isRunning()){ 
			gameIsRunning = false; 
			gameTimer.stop();
		}
		else {
			gameIsRunning = true;
			this.gameTimer.start();
		}
	}
	
	/**
	 * Method for checking whether game is running or not.
	 * 
	 * @return True if game is running else false.
	 */
	public Boolean getGameRunning(){
		return this.gameTimer.isRunning();
	}
	
	/**
	 * Method for checking if the game is over.
	 * 
	 * @return True = game is over. False = game is in play.
	 */
	public Boolean isGameOver(){
		return this.gameIsOn;		
	}
	
	/**
	 * Method for checking whether game is running or not.
	 * 
	 * @return True if game is running else false.
	 */
	public Boolean isGameRunning(){
		return this.gameIsRunning;
	}
	
	/**
	 * Method for getting the total amount of balls currently in play.
	 * 
	 * @return The amount as an int value.
	 */
	public int getAmountOfBalls(){ return ballsInAir; }

	/**
	 * Can be called after the game is over to see why it is over. (No more balls, all bricks destroyed)
	 * 
	 * @return Exit reason.
	 */
	public int getExitReason() {
		return exitReason;
	}

	
	
	private class gameLoop implements ActionListener{
		
		private void gameIteration(){
		int index, amountOfBalls, brickCounter;
		ArrayList<toRemove> deleteUs = new ArrayList<toRemove>();
		GfxElement tmpGfx;
		
		
	
			amountOfBalls = 0;
			brickCounter = 0;
		
			//Update positions and let ball(s) check for collisions
			for(GfxElement elem : gfxElements){
					elem.move();
					
					if(elem.getKillMe()) deleteUs.add(new toRemove(gfxElements.indexOf(elem)));
					if(elem instanceof GfxExplossion){
						if( ((GfxExplossion) elem).isDestroyable() ){
								deleteUs.add(new toRemove(gfxElements.indexOf(elem)));
						}
					}
					if(elem instanceof GBall){
						// .bounceCheck returns the index number of the gfxElement the ball
						// has destroyed (if any). Otherwise it returns -1
						index = ((GBall) elem).bounceCheck();
						if(index>-1) deleteUs.add(new toRemove(index));
						
						amountOfBalls++;
					}
					
					if(elem instanceof GBrick)
						brickCounter++;
					
					//see if surprise brick should stay alive
					if(elem instanceof GSupriseBrick){
						if( ((GSupriseBrick) elem).removeSupriseBrick() || ((GSupriseBrick) elem).controllerIntersectCheck() ){
							index = gfxElements.indexOf(elem);
							deleteUs.add(new toRemove(index));
						}
						
					}
					
			}
			
			// Elements MUST be removed outside of the for:each loop!
			int lastRemoved = -1;   // Used to avoid exception when two balls hit the same brick
									// at the same time
			if(deleteUs.size() > 0){
				Collections.sort(deleteUs);
				

				for(toRemove r : deleteUs){
					
					if(lastRemoved != r.index){
						lastRemoved = r.index;
						tmpGfx = gfxElements.remove(r.index);
	
						if( tmpGfx instanceof GBrick && !(tmpGfx instanceof GSupriseBrick)){	
							GSupriseBrick elemSuprisebrick=gSP.newSupriceBrick(tmpGfx.getX(), tmpGfx.getY()); 
							
							GfxExplossion e = new GfxExplossion((float) tmpGfx.getX(), (float) tmpGfx.getY(), Color.white, 1);
							gfxElements.add(e);
							
							if(elemSuprisebrick != null){	
								gfxElements.add(elemSuprisebrick);
							}
						}
					}
				}
			}
			deleteUs.clear();
			ballsInAir = amountOfBalls;
			bricksLeft = brickCounter;
			
			
			if(amountOfBalls == 0 || brickCounter == 0){
				if(amountOfBalls == 0) exitReason = OUTOFBALLS;
				if(brickCounter == 0) exitReason = BRICKSCLEARED;
				gameIsOn = false;
				toggleOnOff();
			}
	
		//Call for redraw
			adapter.getGameRenderer().updateScreen();
	}

		@Override
		public void actionPerformed(ActionEvent arg0) {
			gameIteration();
			
		} 
	}	
}
	


